
package model;

import java.awt.Polygon;
import java.awt.geom.Line2D;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.util.ArrayList;
import java.util.Iterator;

/**
 *
 * @author Beto
 */
public class GrafoVisibilidade {

    private Point2D pontoInicial, pontoFinal;
    private ArrayList<Polygon> obstaculos;
    private ArrayList<Point2D> listaPontos;
    private GrafoPonderado grafoPonderado;
    private int numVertices;

    
    public GrafoVisibilidade(Point2D pontoInicial, Point2D pontoFinal, ArrayList<Polygon> obstaculos) {
        this.pontoInicial = pontoInicial;
        this.pontoFinal   = pontoFinal;
        this.obstaculos   = obstaculos;
        
        this.listaPontos = new ArrayList<Point2D>();

        listaPontos.add(this.pontoInicial);
        for (Polygon polygon : this.obstaculos)
            for(int i = 0; i < polygon.npoints; i++)
                listaPontos.add(new Point2D.Double(polygon.xpoints[i], polygon.ypoints[i]));
        listaPontos.add(this.pontoFinal);

        this.numVertices = listaPontos.size();
        Point2D[] pontosAux = new Point2D[numVertices];
        this.grafoPonderado = new GrafoPonderado(listaPontos.toArray(pontosAux));
    }

    public GrafoPonderado gerarGrafoVisibilidade() {
        
        ArrayList<Line2D.Double> arestas_diagonais = new ArrayList<Line2D.Double>();
        
        // Organizando as arestas dos obstáculos
        for (Polygon polygon : obstaculos) {
            for(int i = 0; i < polygon.npoints-1; i++) {
                arestas_diagonais.add(new Line2D.Double(polygon.xpoints[i], polygon.ypoints[i],
                                                polygon.xpoints[i+1], polygon.ypoints[i+1]));
            }
            arestas_diagonais.add(new Line2D.Double(polygon.xpoints[polygon.npoints-1], polygon.ypoints[polygon.npoints-1],
                                    polygon.xpoints[0], polygon.ypoints[0]));
            arestas_diagonais.add(new Line2D.Double(polygon.xpoints[0], polygon.ypoints[0], 
                                    polygon.xpoints[2], polygon.ypoints[2]));
            arestas_diagonais.add(new Line2D.Double(polygon.xpoints[1], polygon.ypoints[1], 
                                    polygon.xpoints[3], polygon.ypoints[3]));
        }

        
        // Gerando arcos
        Iterator<Line2D.Double> it = arestas_diagonais.iterator();
        boolean intersectou = false;
        int contLinhas = 0;

        for (int i = 0; i < numVertices; i++) {
            Point2D pontoAtual = listaPontos.get(i);
            
            for (int j = i+1; j < numVertices; j++) {
                Line2D.Double linhaAtual = new Line2D.Double(pontoAtual, listaPontos.get(j));
                
                if(isArestaObstaculo(it, linhaAtual)) {
                    grafoPonderado.adicionarArco(i, j);
                    contLinhas++;
                }
                else {
                    for (Line2D.Double aresta : arestas_diagonais) {
                        if(intersecta(linhaAtual, aresta))
                            intersectou = true;
                    }

                    if(!intersectou) { grafoPonderado.adicionarArco(i, j); contLinhas++; }
                    intersectou = false;
                }
            }
        }

        return grafoPonderado;
    }

   
    private boolean isArestaObstaculo(Iterator<Line2D.Double> it, Line2D linha) {
        while(it.hasNext()) {
            Line2D.Double l = it.next();
            
            if((l.getP1().distance(linha.getP1()) == 0 && l.getP2().distance(linha.getP2()) == 0) ||
                    (l.getP1().distance(linha.getP2()) == 0 && l.getP2().distance(linha.getP1()) == 0))
                return true;
        }
        return false;
    }
    
    private boolean intersecta(Line2D.Double linha1, Line2D.Double linha2) {
        if(!linha1.intersectsLine(linha2))
            return false;
        else {
            if(linha1.getP1().distance(linha2.getP1()) == 0 || linha1.getP1().distance(linha2.getP2()) == 0 ||
                    linha1.getP2().distance(linha2.getP1()) == 0 || linha1.getP2().distance(linha2.getP2()) == 0)
                return false;
            else
                return true;
        }
    }    

    public int getNumVertices() {
        return numVertices;
    }

    public ArrayList<Point2D> getListaPontos() {
        return listaPontos;
    }
}
